package sk.ownage.p1.featurestore;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.os.Environment;
import android.util.Log;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;

public class LandmarkMapOverlay {
	final String TAG = "LandmarkMapOverlay";
	final LatLngBounds boundingBox = new LatLngBounds(
			new LatLng(-85.0, -180.0), new LatLng(85.0, 179.999999));
	static final int DIRECTORY_MAX = LandmarkStore.DIRECTORY_MAX;
	final String rootPath = Environment.getExternalStorageDirectory()
			+ "/geometryStore" + "/landmarks";
	private String landmarkRootPath="";
	int threshold = LandmarkQuadTreeRoot.actualThreshold;
	int actualZoomLevel=0;
	Map<String, List<Marker>> actualLandmarkMarkersLists = new HashMap<String, List<Marker>>();
	Map<String, Long> pathsLastModificationTime = new HashMap<String, Long>();
	List<Polyline> actualMarkerBoundingPolylines = new ArrayList<Polyline>();
	Set<String> allNewLandmarkMarkersListsPaths = new HashSet<String>();
	String actualPath="";
	
	public LandmarkMapOverlay(int trackId){
		this(trackId,"");
	} 
	
	public LandmarkMapOverlay(int landmarkId, String landmarkRootPath){
		this.landmarkRootPath=landmarkRootPath;
		int folderId = landmarkId / DIRECTORY_MAX;
		int fileId = landmarkId % DIRECTORY_MAX;
		String folderPath = (((folderId / 100) > 0) ? "" : "0")
				+ (((folderId / 10) > 0) ? "" : "0") + folderId;
		String filePath = (((fileId / 100) > 0) ? "" : "0")
				+ (((fileId / 10) > 0) ? "" : "0") + fileId;
		actualPath = ((!this.landmarkRootPath.equals(""))?this.landmarkRootPath:rootPath) + "/" + folderPath + "/" + filePath;
	} 
	
	public void cleanMap() {
		for (List<Marker> markerList : actualLandmarkMarkersLists.values()) {
			if (markerList != null)
				for (Marker marker : markerList) {
					marker.remove();
				}
		}
		actualLandmarkMarkersLists = new HashMap<String, List<Marker>>();
	}

	public void drawLandmark(GoogleMap gmap, BitmapDescriptor icon) {
		File geometryFile=new File(actualPath+"/"+actualZoomLevel+"/size.bin");
		if(!geometryFile.exists())
			return;
		else{
			try {
				FileInputStream fis = new FileInputStream(geometryFile);
				ObjectInputStream is = new ObjectInputStream(
						new BufferedInputStream(fis));
				is.readInt();
				is.readBoolean();
				is.readLong();
				is.readBoolean();
				threshold = is.readInt();
				is.close();
			} catch (Exception e) {
				Log.d(TAG, e.toString());
			}
		}
		int newZoomLevel=((int)gmap.getCameraPosition().zoom-4)/3;
		newZoomLevel=(newZoomLevel>4)?4:newZoomLevel;
		if(actualZoomLevel!=newZoomLevel){
			cleanMap();
			actualZoomLevel=newZoomLevel;
		}
		for (Polyline polyline : actualMarkerBoundingPolylines) {
			polyline.remove();
		}
		actualMarkerBoundingPolylines=new ArrayList<Polyline>();
		allNewLandmarkMarkersListsPaths = new HashSet<String>();
		Map<String, List<Marker>> result = getMarkers(gmap.getProjection()
				.getVisibleRegion().latLngBounds, "", boundingBox.southwest,
				boundingBox.northeast, gmap, icon);
		List<String> to_remove = new ArrayList<String>();
		for (String path : actualLandmarkMarkersLists.keySet()) {
			if (!allNewLandmarkMarkersListsPaths.contains(path)) {
				to_remove.add(path);
			}
		}
		for (String path : to_remove) {
			pathsLastModificationTime.remove(path);
			for (Marker marker : actualLandmarkMarkersLists.get(path))
				marker.remove();
			actualLandmarkMarkersLists.remove(path);
		}
		actualLandmarkMarkersLists.putAll(result);
		int count=0;
		for (String key : actualLandmarkMarkersLists.keySet()) {
			count+=actualLandmarkMarkersLists.get(key).size();
		}
		Log.d(TAG,"count >>> "+ count);
	}

	public Map<String, List<Marker>> getMarkers(LatLngBounds boundingBox1,
			String _path, LatLng _SW, LatLng _NE, GoogleMap gmap, BitmapDescriptor icon) {

		// Log.d(TAG,"vstup do getMarkers");
		double averageLat = (_SW.latitude + _NE.latitude) / 2;
		double averageLon = (_SW.longitude + _NE.longitude) / 2;
		LatLng tempCenter = new LatLng(averageLat, averageLon);

		Map<String, List<Marker>> result = new HashMap<String, List<Marker>>();

		LatLngBounds bbox2 = new LatLngBounds(_SW, _NE);
		boolean condition = (boundingBox1.contains(new LatLng(_NE.latitude,
				_SW.longitude))
				|| boundingBox1
						.contains(new LatLng(_SW.latitude, _NE.longitude))
				|| boundingBox1.contains(_NE)
				|| boundingBox1.contains(_SW)
				|| boundingBox1.contains(tempCenter)
				|| bbox2.contains(boundingBox1.northeast)
				|| bbox2.contains(boundingBox1.southwest)
				|| bbox2.contains(new LatLng(boundingBox1.southwest.latitude,
						boundingBox1.northeast.longitude)) || bbox2
				.contains(new LatLng(boundingBox1.northeast.latitude,
						boundingBox1.southwest.longitude)));
		if (!condition)
			return result;
		FileInputStream fis;
		ObjectInputStream is;
		if (_path.equals(""))
			_path = actualPath+"/"+actualZoomLevel;
		File sizePath = new File(_path + "/" + "size.bin");
		int flag = 0;
		if (sizePath.exists())
			try {
				fis = new FileInputStream(sizePath);
				is = new ObjectInputStream(new BufferedInputStream(fis));
				flag = is.readInt();
				is.close();
			} catch (Exception e) {
				Log.d(TAG, e.toString());
			}
		File inputStream = new File(_path + "/" + "list.bin");
		if (flag>=threshold) {
			File tempFile;
			tempFile = new File(_path + "/" + 0 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getMarkers(boundingBox1, _path + "/" + 0,
						tempCenter, _NE, gmap, icon));
			tempFile = new File(_path + "/" + 1 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getMarkers(boundingBox1, _path + "/" + 1,
						new LatLng(tempCenter.latitude, _SW.longitude),
						new LatLng(_NE.latitude, tempCenter.longitude), gmap, icon));
			tempFile = new File(_path + "/" + 2 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getMarkers(boundingBox1, _path + "/" + 2,
						new LatLng(_SW.latitude, tempCenter.longitude),
						new LatLng(tempCenter.latitude, _NE.longitude), gmap, icon));
			tempFile = new File(_path + "/" + 3 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getMarkers(boundingBox1, _path + "/" + 3, _SW,
						tempCenter, gmap, icon));

		} else {
			if (actualLandmarkMarkersLists.containsKey(_path)
					&& (pathsLastModificationTime.get(_path) != null && inputStream
							.lastModified() <= pathsLastModificationTime.get(_path)))
				allNewLandmarkMarkersListsPaths.add(_path);
			else
				try {
					pathsLastModificationTime.put(_path, inputStream.lastModified());
					fis = new FileInputStream(inputStream);
					is = new ObjectInputStream(new BufferedInputStream(fis));
					while (flag > 0) {
						if (!result.containsKey(_path))
							result.put(_path, new ArrayList<Marker>());
						allNewLandmarkMarkersListsPaths.add(_path);
						result.get(_path).add(
								gmap.addMarker(new MarkerOptions().position(
										new LatLng(is.readDouble(), is
												.readDouble())).title(
										is.readUTF()).icon(icon)));
						flag--;
					}
				} catch (EOFException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (StreamCorruptedException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (IOException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				}
		}
		return result;
	}

}
